#!/usr/bin/env python3
import pika
import time
import os
import json
from subprocess import Popen, PIPE
import sqlite3
import sys
import loger
import getUrlfile
from PIL import Image

os.chdir("/home/aiot/workspace/EpidemicPreventionProj/EP_faceDbManager")

failedUrlRecordPath = "../record_DbOpErr.txt"
downloadUrlTryTimes = 20 # if download url err, try 100 times
downloadTryDict = {}

def failedUrlRecord(url, reason):
    f = failedUrlRecordPath
    with open(f, "a") as file:
        context0 = "time: " + time.strftime('%Y-%m-%d-%H-%M', time.localtime()) + "\n"
        file.write(context0)
        context1 = "reason: " + reason + "\n"
        file.write(context1)
        context2 = "url: " + url + "\n"
        file.write(context2)
        file.write("\n")


def callback_getcommand(ch, method, properties, body):
    '''
    input: message from passDB manager
    output: message to DBImgQueue
    '''
    global downloadTryDict
    errcode = 0
    log.info("callback_getcmd  Received " + str(body))
    msg = str(body, 'utf-8')

    msgdict = json.loads(msg)
    operate = msgdict.get("operate")
    url = msgdict.get("url")
    faceid = msgdict.get("faceid")

    if operate == "face_add":
        ch.basic_ack(delivery_tag = method.delivery_tag)
        
        # download url to local jpg file
        try:
            localtmpjpgpath = "../EP_imgTmpSpace/" + str(time.time()) + ".jpg"
            getUrlfile.requestImg(url, localtmpjpgpath)
        except:
            errcode = 1
            
        for i in range(5):
            try:
                img = Image.open(localtmpjpgpath)
                wid = img.width
                if wid > 0:
                    break
                else:
                    time.sleep(0.5)
            except:
                time.sleep(0.5)

        if not os.path.exists(localtmpjpgpath):
            log.warning("err! resource can't be download. err! url: " + url)
            errcode = 1

        if errcode == 1:
            if url in downloadTryDict:
                downloadTryDict[url] -= 1
                if downloadTryDict[url] > 0:
                    log.warning("download url err, try again.")
                    log.warning("downloadTryDict: " + str(downloadTryDict))
                    ch.queue_declare(queue='PDMoutQueue')
                    ch.basic_publish(exchange='', routing_key='PDMoutQueue', body=msg)
                else:
                    failedUrlRecord(url, "download url failed")
                    log.error("Sorry! resource can't be download after 100 times try. the url: " + url)
            else:
                log.warning("download failed url add to downloadTryDict.")
                downloadTryDict.update({url:downloadUrlTryTimes})
                ch.queue_declare(queue='PDMoutQueue')
                ch.basic_publish(exchange='', routing_key='PDMoutQueue', body=msg)
        
        if errcode == 0:
            if url in downloadTryDict:
                log.info("download failed url now successfully download:" + url)
                downloadTryDict.pop(url)

            # 生成img to face feature vector调用指令消息内容,发布消息
            dict = {}
            dict.update({'localtmpjpgpath':localtmpjpgpath})
            dict.update({'faceid':faceid})
            dict.update({'operate':operate})
            dict.update({'url':url})
            commandmsg = json.dumps(dict)

            ch.basic_publish(exchange='', routing_key='DbImgpathQueue', body=commandmsg)
            log.info("msg: " + str(commandmsg))
            log.info(" faceadd Sent msg out!")

    elif operate == "face_del": # notice: this del a person's all photos 
        dict = {}
        dict.update({'faceid':faceid})
        dict.update({'operate':operate})
        dict.update({'facefeature':[]})
        commandmsg = json.dumps(dict)

        ch.basic_publish(exchange='', routing_key='DbVecQueue', body=commandmsg)
        log.info("msg: " + str(commandmsg))
        log.info(" faceadd Sent msg out!")

        ch.basic_ack(delivery_tag = method.delivery_tag)
    else:
        failedUrlRecord(url, "unsupported operate")
        log.error("db operate command format err.")
        ch.basic_ack(delivery_tag = method.delivery_tag)


def callback_operateFaceDB(ch, method, properties, body):
    '''
    input: face feature vector
    output: insert face feature vector to db
    '''
    start = time.time()
    msg = str(body, 'utf-8')
    log.info(" callback_opFaceDB Received: " + str(msg[0:100]))
    dict = json.loads(msg)
    faceid = dict["faceid"]
    operate = dict["operate"]
    facefeature = dict["facefeature"]

    con = sqlite3.connect("../EP_faceDB/faceiddatabase.db3")
    cur = con.cursor()
    try:
        cur.execute('CREATE TABLE facedb(face_id TEXT PRIMARY KEY, face_featurevector TEXT)')
        con.commit()
    except:
        log.warning("db is already exists.")

    ifdbchanged = 0 
    curfaceid = str(faceid)
    curfacevec = str(facefeature)
    curoption = str(operate)

    # add face
    if curoption == "face_add":
        try:
            cur.execute("SELECT * FROM facedb WHERE face_id = '%s'" % curfaceid)
            output = cur.fetchall()
            if len(output) >= 5:
                log.warning("current faceid has more than 5 faces already.")
                #continue
                return
        except:
            log.error("select facedb err.")

        try:
            cur.execute("INSERT INTO facedb(face_id, face_featurevector) VALUES('%s', '%s')" % (curfaceid, curfacevec))
            con.commit()
            ifdbchanged = 1
        except:
            log.info("current face id is already exists.")
            cur.execute("SELECT * FROM facedb WHERE face_id = '%s'" % curfaceid)
            output = cur.fetchall()
            log.warning("current face id is already exists.")

    # del all faces of a person
    if curoption == "face_del":
        try:
            cur.execute("DELETE FROM facedb WHERE face_id = '%s'" % curfaceid)
            con.commit()
            ifdbchanged = 1
        except:
            log.error("current person/faceid is not found.")
    con.close()

    # if face db changed, face recognition module should reload db
    if ifdbchanged:
        tmpqueue = channel.queue_declare(queue='DbReloadQueue')
        msgcount = tmpqueue.method.message_count

        if msgcount == 0:
            commandmsg = "reload face db"
            ch.basic_publish(exchange='', routing_key='DbReloadQueue', body=commandmsg)
            log.info("msg: " + str(commandmsg))
            log.info("reload Sent msg out!")

    ch.basic_ack(delivery_tag = method.delivery_tag)
    end = time.time()
    log.info("time cost: %f" % (end - start))


if __name__ == '__main__':
    '''
    发布img imgpath指令至rabbitMQ服务器
    '''
    log = loger.Log("faceDbManager")

    # 建立连接
    credentials = pika.PlainCredentials('aiot', 'aiot')
    Parameter = pika.ConnectionParameters('127.0.0.1', 5672, '/', credentials)
    connection = pika.BlockingConnection(Parameter)
    channel = connection.channel()

    # 声明/指定消息队列
    channel.queue_declare(queue='DbImgpathQueue')
    channel.queue_declare(queue='DbVecQueue')
    channel.queue_declare(queue='DbReloadQueue')
    channel.queue_declare(queue='PDMoutQueue')

    # 配置从rabbitMQ服务器消息队列中取消息的参数
    channel.basic_consume(queue='PDMoutQueue', on_message_callback=callback_getcommand)
    channel.basic_consume(queue='DbVecQueue', on_message_callback=callback_operateFaceDB)

    # 永久持续运行
    log.info('facedbmanager  Waiting for messages. To exit press CTRL+C')
    channel.start_consuming()

